<?php

class Adept_Component_Composite extends Adept_Component_Base 
{
    
    protected $children = array();
    
    static protected $idGenerator = 1;
    
    public function addChild($child, $childId = null) 
    {         
        $child->setParent($this);        
        $child->setRoot($this->getRoot());
        if ($childId != null) {
            if ($this->getRoot()->findChildById($childId)) {
                throw new Adept_Component_Exception("Duplicated 'id' property - '{$childId}'");
            }
            $child->setId($childId);
        } elseif (!$child->getId()) {
            $class = end(explode("_", get_class($child)));
            if (empty($class)) {
                $class = 'component';
            }
            $child->setId(strtolower($class) . self::$idGenerator++);
        }
        
        $this->children[$child->getId()] = $child;
    }    
    
    public function getChild($id) 
    {
        return isset($this->children[$id]) ? $this->children[$id] : null;
    }	
    
    public function getChildren() 
    {
        return $this->children;
    }    
    
    public function findChildById($id, $recursive = true) 
    {
        foreach ($this->children as $childId => $child) {
            if ($childId == $id) {
                return $child;
            }
            if ($recursive && $child instanceof Adept_Component_Composite) {
                $result = $child->findChildById($id, true);
                if ($result != null) {
                    return $result;
                }
            }
        }
        return null;
    }    
    
    public function findChildByClass($class, $recursive = true)
    {
        foreach ($this->children as $child) {
            if ($child instanceof $class) {
                return $child;
            }
            if ($recursive && $child instanceof Adept_Component_Composite) {
                $result = $child->findChildByClass($class, true);
                if ($result != null) {
                    return $result;
                }
            }
        }
        return null;
    }

    public function findChildrenByClass($class, $recursive = true) {
        $result = array();
        foreach ($this->children as $child) {
            if ($child instanceof $class) {
                $result[] = $child;
            }
            if ($recursive && $child instanceof Adept_Component_Composite) {
                $result = array_merge($result, $child->findChildrenByClass($class, true));
            }
        }
        return $result;
    }
    
    public function processRestore() 
    {
        foreach ($this->children as $child) {
            $child->processRestore();
        }
    }    
    
    public function processRequest() 
    {
        foreach ($this->children as $child) {
            $child->processRequest();
        }
    }    
    
    public function processSave()
    {        
        foreach ($this->children as $child) {
            $child->processSave();
        }
    }
    
    public function render() 
    {
        $this->renderBegin();
        $this->renderChildren();
        $this->renderEnd();
    }    
    
    public function renderBegin() 
    {
        if (!$this->isRendered()) {
            return ;
        }
        if ($this->hasRenderer()) {
            $this->getRenderer()->renderBegin($this);
        }
    }    
    
    public function renderChildren() 
    {
        if (!$this->isRendered()) {
            return ;
        }
        if ($this->hasRenderer()) {
            $this->getRenderer()->renderChildren($this);
        } else {
            foreach ($this->children as $child) {
                $child->render();
            }
        }
    }    
    
    public function renderEnd() 
    {
        if (!$this->isRendered()) {
            return ;
        }
        if ($this->hasRenderer()) {
            $this->getRenderer()->renderEnd($this);
        }
    }    
    

    public function setRoot($root)
    {
    	parent::setRoot($root);
    	foreach ($this->children as $child) {
    	    $child->setRoot($child);
    	}
    }
}